<?php

namespace App\Services\Store\User;

use App\Exceptions\ApiException;
use App\Exceptions\SmsException;
use App\Helpers\Redis\RedisHelper;
use App\Services\Applet\BaseAppletService;
use App\Services\Applet\Other\SmsService;
use Closure;
use Exception;

class BaseUserService extends BaseAppletService
{
    protected array $appLoginSmsWhiteList = [
        '17785738736',
    ];

    /**
     * 检测手机号
     *
     * @param string $tel
     * @throws ApiException
     */
    protected function checkTel(string $tel)
    {
        // 验证手机号
        if (!checkValidMobile($tel)) {
            throw new ApiException('手机号格式不正确', 2013);
        }
    }

    /**
     * 检测白名单
     * 使用场景： 登录、糖果雨入口
     *
     * @param string $tel
     * @return bool
     */
    protected function checkTelWhiteList(string $tel): bool
    {
        return in_array($tel, $this->appLoginSmsWhiteList);
    }

    /**
     * 检测验证码的尝试次数
     *
     * @tips 每小时内只能获取5次验证码，以首次获取验证码时间开始计算，超出后再次获取时不再发送，待1小时后恢复
     *
     * @param string $tel
     * @param Closure $closure
     */
    public function checkCodeTryCount(string $tel, Closure $closure)
    {
        // 获取获取缓存的次数
        $tryCountKey = $this->getVerifyCodeTryCountKey($tel);
        $tryCount = (int)RedisHelper::get($tryCountKey);
        if ($tryCount >= 5) {
            // 执行闭包
            $closure();
        }

        // 累计次数
        $count = RedisHelper::INCR($tryCountKey);
        if ($count == 1) {
            // 如果是第一次计数则初始化过期时间
            RedisHelper::EXPIRE($tryCountKey, SECONDS_HOUR);
        }
    }

    /**
     * 获取验证码重试次数key
     *
     * @param string $tel
     * @return string
     */
    private function getVerifyCodeTryCountKey(string $tel): string
    {
        return sprintf(RedisHelper::STRING_VERIFY_CODE_TRY_COUNT, $tel);
    }

    /**
     * 设置验证码缓存
     *
     * @param string $tel
     * @param string $code
     * @param int|float $expired
     */
    public function setCodeCache(string $tel, string $code, int $expired = SMS_CODE_CACHE_EXPIRED_MINUTE * ONE_MINUTE)
    {
        // 验证码
        $codeKey = $this->getVerifyCodeCacheKey($tel);
        // 设置redis缓存
        RedisHelper::set($codeKey, $code, $expired);
    }

    /**
     * 获取验证码缓存key
     *
     * @param string $tel
     * @return string
     */
    private function getVerifyCodeCacheKey(string $tel): string
    {
        return sprintf(RedisHelper::STRING_VERIFY_CODE, $tel);
    }

    /**
     * @return SmsService
     * @throws Exception
     */
    protected function getSmsService(): SmsService
    {
        return loadService('Applet\Other\SmsService');
    }

    /**
     * 获取验证码验证次数key
     *
     * @param string $tel
     * @return string
     */
    private function getVerifyCodeCheckCountKey(string $tel): string
    {
        return sprintf(RedisHelper::STRING_VERIFY_CODE_CHECK_COUNT, $tel);
    }

    /**
     * 检测验证码的验证次数
     *
     * @tips 在一分钟内输入验证码超过5次
     *
     * @param int $codeType
     * @param string $tel
     * @param Closure $closure
     */
    public function checkCodeCheckCount(int $codeType, string $tel, Closure $closure)
    {
        // 获取获取缓存的次数
        $tryCountKey = $this->getVerifyCodeCheckCountKey($tel);
        $tryCount = (int)RedisHelper::get($tryCountKey);
        if ($tryCount >= 5) {
            // 删除:验证码缓存
            RedisHelper::del($this->getVerifyCodeCacheKey($tel));

            // 执行闭包
            $closure();
        }

        // 累计次数
        $count = RedisHelper::INCR($tryCountKey);
        if ($count == 1) {
            // 如果是第一次计数则初始化过期时间
            RedisHelper::EXPIRE($tryCountKey, ONE_MINUTE);
        }
    }

    /**
     * 检测验证码缓存
     *
     * @param string $tel 手机号
     * @param string $code 验证码
     * @throws SmsException
     */
    public function checkCodeCache(string $tel, string $code)
    {
        // 获取缓存的验证码key
        $key = $this->getVerifyCodeCacheKey($tel);

        $this->checkVerifyCodeCache($key, $tel, $code);
    }

    /**
     * 检测验证码缓存
     *
     * @param string $tel 手机号
     * @param string $code 验证码
     * @throws SmsException
     */
    private function checkVerifyCodeCache(string $key, string $tel, string $code)
    {
        // 获取缓存的验证码
        $cacheCode = RedisHelper::get($key);
        if (!$cacheCode) {
            throw new SmsException('验证码失效！|' . json_encode(['tel' => $tel, 'code' => $code, 'cacheCode' => $cacheCode]), SmsException::CODE_1402);
        }
        if ($code != $cacheCode) {
            throw new SmsException('验证码错误！|' . json_encode(['tel' => $tel, 'code' => $code, 'cacheCode' => $cacheCode]), SmsException::CODE_1401);
        }

        RedisHelper::del($key);
    }
}
